#pragma once

#include "Gate.h"

#include "InteractionOperand.h"
#include "NamedElement.h"
#include "Namespace.h"

namespace uml {

Gate::Gate(const QString& umlType)
    : NamedElement(umlType) {}

void Gate::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    NamedElement::fromJson(json, errorList);
    MessageEnd::fromJson(json, errorList);
}

void Gate::toJson(QJsonObject& json) {
    NamedElement::toJson(json);
    MessageEnd::toJson(json);
}

/// <summary>
/// isOutsideCF
    /// This query returns true if this Gate is attached to the boundary of a
    /// CombinedFragment, and its other end (if present)  is outside of the same
    /// CombinedFragment.
/// </summary>
    /// <code>
    /// result = (self.oppositeEnd()-> notEmpty() and combinedFragment->notEmpty() implies
    /// let oppEnd : MessageEnd = self.oppositeEnd()->asOrderedSet()->first() in
    /// if oppEnd.oclIsKindOf(MessageOccurrenceSpecification) 
    /// then let oppMOS : MessageOccurrenceSpecification = oppEnd.oclAsType(MessageOccurrenceSpecification)
    /// in  self.combinedFragment.enclosingInteraction.oclAsType(InteractionFragment)->asSet()->
    ///      union(self.combinedFragment.enclosingOperand.oclAsType(InteractionFragment)->asSet()) =
    ///      oppMOS.enclosingInteraction.oclAsType(InteractionFragment)->asSet()->
    ///      union(oppMOS.enclosingOperand.oclAsType(InteractionFragment)->asSet())
    /// else let oppGate : Gate = oppEnd.oclAsType(Gate) 
    /// in self.combinedFragment.enclosingInteraction.oclAsType(InteractionFragment)->asSet()->
    ///      union(self.combinedFragment.enclosingOperand.oclAsType(InteractionFragment)->asSet()) =
    ///      oppGate.combinedFragment.enclosingInteraction.oclAsType(InteractionFragment)->asSet()->
    ///      union(oppGate.combinedFragment.enclosingOperand.oclAsType(InteractionFragment)->asSet())
    /// endif)
    /// </code>
bool Gate::isOutsideCF() const
{
    return false;
}

/// <summary>
/// isInsideCF
    /// This query returns true if this Gate is attached to the boundary of a
    /// CombinedFragment, and its other end (if present) is inside of an
    /// InteractionOperator of the same CombinedFragment.
/// </summary>
    /// <code>
    /// result = (self.oppositeEnd()-> notEmpty() and combinedFragment->notEmpty() implies
    /// let oppEnd : MessageEnd = self.oppositeEnd()->asOrderedSet()->first() in
    /// if oppEnd.oclIsKindOf(MessageOccurrenceSpecification)
    /// then let oppMOS : MessageOccurrenceSpecification
    /// = oppEnd.oclAsType(MessageOccurrenceSpecification)
    /// in combinedFragment = oppMOS.enclosingOperand.combinedFragment
    /// else let oppGate : Gate = oppEnd.oclAsType(Gate)
    /// in combinedFragment = oppGate.combinedFragment.enclosingOperand.combinedFragment
    /// endif)
    /// </code>
bool Gate::isInsideCF() const
{
    return false;
}

/// <summary>
/// isActual
    /// This query returns true value if this Gate is an actualGate of an
    /// InteractionUse.
/// </summary>
    /// <code>
    /// result = (interactionUse->notEmpty())
    /// </code>
bool Gate::isActual() const
{
    return false;
}

/// <summary>
/// isFormal
    /// This query returns true if this Gate is a formalGate of an Interaction.
/// </summary>
    /// <code>
    /// result = (interaction->notEmpty())
    /// </code>
bool Gate::isFormal() const
{
    return false;
}

/// <summary>
/// getName
    /// This query returns the name of the gate, either the explicit name (.name) or the
    /// constructed name ('out_" or 'in_' concatenated in front of .message.name) if the
    /// explicit name is not present.
/// </summary>
    /// <code>
    /// result = (if name->notEmpty() then name->asOrderedSet()->first()
    /// else  if isActual() or isOutsideCF() 
    ///   then if isSend() 
    ///     then 'out_'.concat(self.message.name->asOrderedSet()->first())
    ///     else 'in_'.concat(self.message.name->asOrderedSet()->first())
    ///     endif
    ///   else if isSend()
    ///     then 'in_'.concat(self.message.name->asOrderedSet()->first())
    ///     else 'out_'.concat(self.message.name->asOrderedSet()->first())
    ///     endif
    ///   endif
    /// endif)
    /// </code>
QString Gate::getName() const
{
    return "";
}

/// <summary>
/// matches
    /// This query returns true if the name of this Gate matches the name of the in
    /// parameter Gate, and the messages for the two Gates correspond. The Message for
    /// one Gate (say A) corresponds to the Message for another Gate (say B) if (A and B
    /// have the same name value) and (if A is a sendEvent then B is a receiveEvent) and
    /// (if A is a receiveEvent then B is a sendEvent) and (A and B have the same
    /// messageSort value) and (A and B have the same signature value).
/// </summary>
    /// <code>
    /// result = (self.getName() = gateToMatch.getName() and 
    /// self.message.messageSort = gateToMatch.message.messageSort and
    /// self.message.name = gateToMatch.message.name and
    /// self.message.sendEvent->includes(self) implies gateToMatch.message.receiveEvent->includes(gateToMatch)  and
    /// self.message.receiveEvent->includes(self) implies gateToMatch.message.sendEvent->includes(gateToMatch) and
    /// self.message.signature = gateToMatch.message.signature)
    /// </code>
bool Gate::matches(shared_ptr<Gate> gateToMatch) const
{
    return false;
}

/// <summary>
/// isDistinguishableFrom
    /// The query isDistinguishableFrom() specifies that two Gates may coexist in the
    /// same Namespace, without an explicit name property. The association end
    /// formalGate subsets ownedElement, and since the Gate name attribute
    /// is
    /// optional, it is allowed to have two formal gates without an explicit name, but
    /// having derived names which are distinct.
/// </summary>
    /// <code>
    /// result = (true)
    /// </code>
bool Gate::isDistinguishableFrom(shared_ptr<NamedElement> n, shared_ptr<Namespace> ns) const
{
    return false;
}

/// <summary>
/// getOperand
    /// If the Gate is an inside Combined Fragment Gate, this operation returns the
    /// InteractionOperand that the opposite end of this Gate is included within.
/// </summary>
    /// <code>
    /// result = (if isInsideCF() then
    ///   let oppEnd : MessageEnd = self.oppositeEnd()->asOrderedSet()->first() in
    ///     if oppEnd.oclIsKindOf(MessageOccurrenceSpecification)
    ///     then let oppMOS : MessageOccurrenceSpecification = oppEnd.oclAsType(MessageOccurrenceSpecification)
    ///         in oppMOS.enclosingOperand->asOrderedSet()->first()
    ///     else let oppGate : Gate = oppEnd.oclAsType(Gate)
    ///         in oppGate.combinedFragment.enclosingOperand->asOrderedSet()->first()
    ///     endif
    ///   else null
    /// endif)
    /// </code>
shared_ptr<InteractionOperand> Gate::getOperand() const
{
    return nullptr;
}

} // namespace uml
